home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / initsnb.zoo / init / sh / parser.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-26  |  27.1 KB  |  1,253 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. static char sccsid[] = "@(#)parser.c    5.3 (Berkeley) 4/12/91";
  39. #endif /* not lint */
  40.  
  41. #ifdef __STDC__
  42. #  define __P(x) x
  43. #else
  44. #  define __P(x)
  45. #endif
  46.  
  47. #include "shell.h"
  48. #include "parser.h"
  49. #include "nodes.h"
  50. #include "expand.h"    /* defines rmescapes() */
  51. #include "redir.h"    /* defines copyfd() */
  52. #include "syntax.h"
  53. #include "options.h"
  54. #include "input.h"
  55. #include "output.h"
  56. #include "var.h"
  57. #include "error.h"
  58. #include "memalloc.h"
  59. #include "mystring.h"
  60.  
  61.  
  62. /*
  63.  * Shell command parser.
  64.  */
  65.  
  66. #define EOFMARKLEN 79
  67.  
  68. /* values returned by readtoken */
  69. #include "token.def"
  70.  
  71.  
  72.  
  73. struct heredoc {
  74.     struct heredoc *next;    /* next here document in list */
  75.     union node *here;        /* redirection node */
  76.     char *eofmark;        /* string indicating end of input */
  77.     int striptabs;        /* if set, strip leading tabs */
  78. };
  79.  
  80.  
  81.  
  82. struct heredoc *heredoclist;    /* list of here documents to read */
  83. int parsebackquote;        /* nonzero if we are inside backquotes */
  84. int doprompt;            /* if set, prompt the user */
  85. int needprompt;            /* true if interactive and at start of line */
  86. int lasttoken;            /* last token read */
  87. MKINIT int tokpushback;        /* last token pushed back */
  88. char *wordtext;            /* text of last word returned by readtoken */
  89. int checkkwd;               /* 1 == check for kwds, 2 == also eat newlines */
  90. struct nodelist *backquotelist;
  91. union node *redirnode;
  92. struct heredoc *heredoc;
  93. int quoteflag;            /* set if (part of) last token was quoted */
  94. int startlinno;            /* line # where last token started */
  95.  
  96.  
  97. #define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
  98. #ifdef GDB_HACK
  99. static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
  100. static const char types[] = "}-+?=";
  101. #endif
  102.  
  103.  
  104. STATIC union node *list __P((int));
  105. STATIC union node *andor __P((void));
  106. STATIC union node *pipeline __P((void));
  107. STATIC union node *command __P((void));
  108. STATIC union node *simplecmd __P((void));
  109. STATIC void parsefname __P((void));
  110. STATIC void parseheredoc __P((void));
  111. STATIC int readtoken __P((void));
  112. STATIC int readtoken1 __P((int, char const *, char *, int));
  113. STATIC void attyline __P((void));
  114. STATIC int noexpand __P((char *));
  115. STATIC void synexpect __P((int));
  116. STATIC void synerror __P((char *));
  117.  
  118. #if ATTY
  119. STATIC void putprompt __P((char *));
  120. #else /* not ATTY */
  121. #define putprompt(s)    out2str(s)
  122. #endif
  123.  
  124.  
  125.  
  126.  
  127. /*
  128.  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
  129.  * valid parse tree indicating a blank line.)
  130.  */
  131.  
  132. union node *
  133. parsecmd(interact) {
  134.     int t;
  135.  
  136.     doprompt = interact;
  137.     if (doprompt)
  138.         putprompt(ps1val());
  139.     needprompt = 0;
  140.     if ((t = readtoken()) == TEOF)
  141.         return NEOF;
  142.     if (t == TNL)
  143.         return NULL;
  144.     tokpushback++;
  145.     return list(1);
  146. }
  147.  
  148.  
  149. STATIC union node *
  150. list(nlflag) {
  151.     union node *n1, *n2, *n3;
  152.  
  153.     checkkwd = 2;
  154.     if (nlflag == 0 && tokendlist[peektoken()])
  155.         return NULL;
  156.     n1 = andor();
  157.     for (;;) {
  158.         switch (readtoken()) {
  159.         case TBACKGND:
  160.             if (n1->type == NCMD || n1->type == NPIPE) {
  161.                 n1->ncmd.backgnd = 1;
  162.             } else if (n1->type == NREDIR) {
  163.                 n1->type = NBACKGND;
  164.             } else {
  165.                 n3 = (union node *)stalloc(sizeof (struct nredir));
  166.                 n3->type = NBACKGND;
  167.                 n3->nredir.n = n1;
  168.                 n3->nredir.redirect = NULL;
  169.                 n1 = n3;
  170.             }
  171.             goto tsemi;
  172.         case TNL:
  173.             tokpushback++;
  174.             /* fall through */
  175. tsemi:        case TSEMI:
  176.             if (readtoken() == TNL) {
  177.                 parseheredoc();
  178.                 if (nlflag)
  179.                     return n1;
  180.             } else {
  181.                 tokpushback++;
  182.             }
  183.             checkkwd = 2;
  184.             if (tokendlist[peektoken()])
  185.                 return n1;
  186.             n2 = andor();
  187.             n3 = (union node *)stalloc(sizeof (struct nbinary));
  188.             n3->type = NSEMI;
  189.             n3->nbinary.ch1 = n1;
  190.             n3->nbinary.ch2 = n2;
  191.             n1 = n3;
  192.             break;
  193.         case TEOF:
  194.             if (heredoclist)
  195.                 parseheredoc();
  196.             else
  197.                 pungetc();        /* push back EOF on input */
  198.             return n1;
  199.         default:
  200.             if (nlflag)
  201.                 synexpect(-1);
  202.             tokpushback++;
  203.             return n1;
  204.         }
  205.     }
  206. }
  207.  
  208.  
  209.  
  210. STATIC union node *
  211. andor() {
  212.     union node *n1, *n2, *n3;
  213.     int t;
  214.  
  215.     n1 = pipeline();
  216.     for (;;) {
  217.         if ((t = readtoken()) == TAND) {
  218.             t = NAND;
  219.         } else if (t == TOR) {
  220.             t = NOR;
  221.         } else {
  222.             tokpushback++;
  223.             return n1;
  224.         }
  225.         n2 = pipeline();
  226.         n3 = (union node *)stalloc(sizeof (struct nbinary));
  227.         n3->type = t;
  228.         n3->nbinary.ch1 = n1;
  229.         n3->nbinary.ch2 = n2;
  230.         n1 = n3;
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. STATIC union node *
  237. pipeline() {
  238.     union node *n1, *pipenode;
  239.     struct nodelist *lp, *prev;
  240.  
  241.     n1 = command();
  242.     if (readtoken() == TPIPE) {
  243.         pipenode = (union node *)stalloc(sizeof (struct npipe));
  244.         pipenode->type = NPIPE;
  245.         pipenode->npipe.backgnd = 0;
  246.         lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  247.         pipenode->npipe.cmdlist = lp;
  248.         lp->n = n1;
  249.         do {
  250.             prev = lp;
  251.             lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  252.             lp->n = command();
  253.             prev->next = lp;
  254.         } while (readtoken() == TPIPE);
  255.         lp->next = NULL;
  256.         n1 = pipenode;
  257.     }
  258.     tokpushback++;
  259.     return n1;
  260. }
  261.  
  262.  
  263.  
  264. STATIC union node *
  265. command() {
  266.     union node *n1, *n2;
  267.     union node *ap, **app;
  268.     union node *cp, **cpp;
  269.     union node *redir, **rpp;
  270.     int t;
  271.  
  272.     checkkwd = 2;
  273.     switch (readtoken()) {
  274.     case TIF:
  275.         n1 = (union node *)stalloc(sizeof (struct nif));
  276.         n1->type = NIF;
  277.         n1->nif.test = list(0);
  278.         if (readtoken() != TTHEN)
  279.             synexpect(TTHEN);
  280.         n1->nif.ifpart = list(0);
  281.         n2 = n1;
  282.         while (readtoken() == TELIF) {
  283.             n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
  284.             n2 = n2->nif.elsepart;
  285.             n2->type = NIF;
  286.             n2->nif.test = list(0);
  287.             if (readtoken() != TTHEN)
  288.                 synexpect(TTHEN);
  289.             n2->nif.ifpart = list(0);
  290.         }
  291.         if (lasttoken == TELSE)
  292.             n2->nif.elsepart = list(0);
  293.         else {
  294.             n2->nif.elsepart = NULL;
  295.             tokpushback++;
  296.         }
  297.         if (readtoken() != TFI)
  298.             synexpect(TFI);
  299.         checkkwd = 1;
  300.         break;
  301.     case TWHILE:
  302.     case TUNTIL: {
  303.         int got;
  304.         n1 = (union node *)stalloc(sizeof (struct nbinary));
  305.         n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
  306.         n1->nbinary.ch1 = list(0);
  307.         if ((got=readtoken()) != TDO) {
  308. TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
  309.             synexpect(TDO);
  310.         }
  311.         n1->nbinary.ch2 = list(0);
  312.         if (readtoken() != TDONE)
  313.             synexpect(TDONE);
  314.         checkkwd = 1;
  315.         break;
  316.     }
  317.     case TFOR:
  318.         if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
  319.             synerror("Bad for loop variable");
  320.         n1 = (union node *)stalloc(sizeof (struct nfor));
  321.         n1->type = NFOR;
  322.         n1->nfor.var = wordtext;
  323.         if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
  324.             app = ≈
  325.             while (readtoken() == TWORD) {
  326.                 n2 = (union node *)stalloc(sizeof (struct narg));
  327.                 n2->type = NARG;
  328.                 n2->narg.text = wordtext;
  329.                 n2->narg.backquote = backquotelist;
  330.                 *app = n2;
  331.                 app = &n2->narg.next;
  332.             }
  333.             *app = NULL;
  334.             n1->nfor.args = ap;
  335.         } else {
  336. #ifndef GDB_HACK
  337.             static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
  338.                                    '@', '=', '\0'};
  339. #endif
  340.             n2 = (union node *)stalloc(sizeof (struct narg));
  341.             n2->type = NARG;
  342.             n2->narg.text = (char *)argvars;
  343.             n2->narg.backquote = NULL;
  344.             n2->narg.next = NULL;
  345.             n1->nfor.args = n2;
  346.         }
  347.         if (lasttoken != TNL && lasttoken != TSEMI)
  348.             synexpect(-1);
  349.         checkkwd = 2;
  350.         if ((t = readtoken()) == TDO)
  351.             t = TDONE;
  352.         else if (t == TBEGIN)
  353.             t = TEND;
  354.         else
  355.             synexpect(-1);
  356.         n1->nfor.body = list(0);
  357.         if (readtoken() != t)
  358.             synexpect(t);
  359.         checkkwd = 1;
  360.         break;
  361.     case TCASE:
  362.         n1 = (union node *)stalloc(sizeof (struct ncase));
  363.         n1->type = NCASE;
  364.         if (readtoken() != TWORD)
  365.             synexpect(TWORD);
  366.         n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
  367.         n2->type = NARG;
  368.         n2->narg.text = wordtext;
  369.         n2->narg.backquote = backquotelist;
  370.         n2->narg.next = NULL;
  371.         while (readtoken() == TNL);
  372.         if (lasttoken != TWORD || ! equal(wordtext, "in"))
  373.             synerror("expecting \"in\"");
  374.         cpp = &n1->ncase.cases;
  375.         while (checkkwd = 2, readtoken() == TWORD) {
  376.             *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
  377.             cp->type = NCLIST;
  378.             app = &cp->nclist.pattern;
  379.             for (;;) {
  380.                 *app = ap = (union node *)stalloc(sizeof (struct narg));
  381.                 ap->type = NARG;
  382.                 ap->narg.text = wordtext;
  383.                 ap->narg.backquote = backquotelist;
  384.                 if (readtoken() != TPIPE)
  385.                     break;
  386.                 app = &ap->narg.next;
  387.                 if (readtoken() != TWORD)
  388.                     synexpect(TWORD);
  389.             }
  390.             ap->narg.next = NULL;
  391.             if (lasttoken != TRP)
  392.                 synexpect(TRP);
  393.             cp->nclist.body = list(0);
  394.             if ((t = readtoken()) == TESAC)
  395.                 tokpushback++;
  396.             else if (t != TENDCASE)
  397.                 synexpect(TENDCASE);
  398.             cpp = &cp->nclist.next;
  399.         }
  400.         *cpp = NULL;
  401.         if (lasttoken != TESAC)
  402.             synexpect(TESAC);
  403.         checkkwd = 1;
  404.         break;
  405.     case TLP:
  406.         n1 = (union node *)stalloc(sizeof (struct nredir));
  407.         n1->type = NSUBSHELL;
  408.         n1->nredir.n = list(0);
  409.         n1->nredir.redirect = NULL;
  410.         if (readtoken() != TRP)
  411.             synexpect(TRP);
  412.         checkkwd = 1;
  413.         break;
  414.     case TBEGIN:
  415.         n1 = list(0);
  416.         if (readtoken() != TEND)
  417.             synexpect(TEND);
  418.         checkkwd = 1;
  419.         break;
  420.     case TWORD:
  421.     case TREDIR:
  422.         tokpushback++;
  423.         return simplecmd();
  424.     default:
  425.         synexpect(-1);
  426.     }
  427.  
  428.     /* Now check for redirection which may follow command */
  429.     rpp = &redir;
  430.     while (readtoken() == TREDIR) {
  431.         *rpp = n2 = redirnode;
  432.         rpp = &n2->nfile.next;
  433.         parsefname();
  434.     }
  435.     tokpushback++;
  436.     *rpp = NULL;
  437.     if (redir) {
  438.         if (n1->type != NSUBSHELL) {
  439.             n2 = (union node *)stalloc(sizeof (struct nredir));
  440.             n2->type = NREDIR;
  441.             n2->nredir.n = n1;
  442.             n1 = n2;
  443.         }
  444.         n1->nredir.redirect = redir;
  445.     }
  446.     return n1;
  447. }
  448.  
  449.  
  450. STATIC union node *
  451. simplecmd() {
  452.     union node *args, **app;
  453.     union node *redir, **rpp;
  454.     union node *n;
  455.  
  456.     args = NULL;
  457.     app = &args;
  458.     rpp = &redir;
  459.     for (;;) {
  460.         if (readtoken() == TWORD) {
  461.             n = (union node *)stalloc(sizeof (struct narg));
  462.             n->type = NARG;
  463.             n->narg.text = wordtext;
  464.             n->narg.backquote = backquotelist;
  465.             *app = n;
  466.             app = &n->narg.next;
  467.         } else if (lasttoken == TREDIR) {
  468.             *rpp = n = redirnode;
  469.             rpp = &n->nfile.next;
  470.             parsefname();    /* read name of redirection file */
  471.         } else if (lasttoken == TLP && app == &args->narg.next
  472.                         && rpp == &redir) {
  473.             /* We have a function */
  474.             if (readtoken() != TRP)
  475.                 synexpect(TRP);
  476. #ifdef notdef
  477.             if (! goodname(n->narg.text))
  478.                 synerror("Bad function name");
  479. #endif
  480.             n->type = NDEFUN;
  481.             n->narg.next = command();
  482.             return n;
  483.         } else {
  484.             tokpushback++;
  485.             break;
  486.         }
  487.     }
  488.     *app = NULL;
  489.     *rpp = NULL;
  490.     n = (union node *)stalloc(sizeof (struct ncmd));
  491.     n->type = NCMD;
  492.     n->ncmd.backgnd = 0;
  493.     n->ncmd.args = args;
  494.     n->ncmd.redirect = redir;
  495.     return n;
  496. }
  497.  
  498.  
  499. STATIC void
  500. parsefname() {
  501.     union node *n = redirnode;
  502.  
  503.     if (readtoken() != TWORD)
  504.         synexpect(-1);
  505.     if (n->type == NHERE) {
  506.         struct heredoc *here = heredoc;
  507.         struct heredoc *p;
  508.         int i;
  509.  
  510.         if (quoteflag == 0)
  511.             n->type = NXHERE;
  512.         TRACE(("Here document %d\n", n->type));
  513.         if (here->striptabs) {
  514.             while (*wordtext == '\t')
  515.                 wordtext++;
  516.         }
  517.         if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
  518.             synerror("Illegal eof marker for << redirection");
  519.         rmescapes(wordtext);
  520.         here->eofmark = wordtext;
  521.         here->next = NULL;
  522.         if (heredoclist == NULL)
  523.             heredoclist = here;
  524.         else {
  525.             for (p = heredoclist ; p->next ; p = p->next);
  526.             p->next = here;
  527.         }
  528.     } else if (n->type == NTOFD || n->type == NFROMFD) {
  529.         if (is_digit(wordtext[0]))
  530.             n->ndup.dupfd = digit_val(wordtext[0]);
  531.         else if (wordtext[0] == '-')
  532.             n->ndup.dupfd = -1;
  533.         else
  534.             goto bad;
  535.         if (wordtext[1] != '\0') {
  536. bad:
  537.             synerror("Bad fd number");
  538.         }
  539.     } else {
  540.         n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
  541.         n = n->nfile.fname;
  542.         n->type = NARG;
  543.         n->narg.next = NULL;
  544.         n->narg.text = wordtext;
  545.         n->narg.backquote = backquotelist;
  546.     }
  547. }
  548.  
  549.  
  550. /*
  551.  * Input any here documents.
  552.  */
  553.  
  554. STATIC void
  555. parseheredoc() {
  556.     struct heredoc *here;
  557.     union node *n;
  558.  
  559.     while (heredoclist) {
  560.         here = heredoclist;
  561.         heredoclist = here->next;
  562.         if (needprompt) {
  563.             putprompt(ps2val());
  564.             needprompt = 0;
  565.         }
  566.         readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
  567.                 here->eofmark, here->striptabs);
  568.         n = (union node *)stalloc(sizeof (struct narg));
  569.         n->narg.type = NARG;
  570.         n->narg.next = NULL;
  571.         n->narg.text = wordtext;
  572.         n->narg.backquote = backquotelist;
  573.         here->here->nhere.doc = n;
  574.     }
  575. }
  576.  
  577. STATIC int
  578. peektoken() {
  579.     int t;
  580.  
  581.     t = readtoken();
  582.     tokpushback++;
  583.     return (t);
  584. }
  585.  
  586. STATIC int xxreadtoken();
  587.  
  588. STATIC int
  589. readtoken() {
  590.     int t;
  591. #ifdef DEBUG
  592.     int alreadyseen = tokpushback;
  593. #endif
  594.     
  595.     t = xxreadtoken();
  596.  
  597.     if (checkkwd) {
  598.         /*
  599.          * eat newlines
  600.          */
  601.         if (checkkwd == 2) {
  602.             checkkwd = 0;
  603.             while (t == TNL) {
  604.                 parseheredoc();
  605.                 t = xxreadtoken();
  606.             }
  607.         } else
  608.             checkkwd = 0;
  609.         /*
  610.          * check for keywords
  611.          */
  612.         if (t == TWORD && !quoteflag) {
  613.             register char **pp;
  614.  
  615.             for (pp = parsekwd; *pp; pp++) {
  616.                 if (**pp == *wordtext && equal(*pp, wordtext)) {
  617.                     lasttoken = t = pp - parsekwd + KWDOFFSET;
  618.                     TRACE(("keyword %s recognized\n", tokname[t]));
  619.                     break;
  620.                 }
  621.             }
  622.         }
  623.     }
  624. #ifdef DEBUG
  625.     if (!alreadyseen)
  626.         TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
  627.     else
  628.         TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
  629. #endif
  630.     return (t);
  631. }
  632.  
  633.  
  634. /*
  635.  * Read the next input token.
  636.  * If the token is a word, we set backquotelist to the list of cmds in
  637.  *    backquotes.  We set quoteflag to true if any part of the word was
  638.  *    quoted.
  639.  * If the token is TREDIR, then we set redirnode to a structure containing
  640.  *    the redirection.
  641.  * In all cases, the variable startlinno is set to the number of the line
  642.  *    on which the token starts.
  643.  *
  644.  * [Change comment:  here documents and internal procedures]
  645.  * [Readtoken shouldn't have any arguments.  Perhaps we should make the
  646.  *  word parsing code into a separate routine.  In this case, readtoken
  647.  *  doesn't need to have any internal procedures, but parseword does.
  648.  *  We could also make parseoperator in essence the main routine, and
  649.  *  have parseword (readtoken1?) handle both words and redirection.]
  650.  */
  651.  
  652. #define RETURN(token)    return lasttoken = token
  653.  
  654. STATIC int
  655. xxreadtoken() {
  656.     register c;
  657.  
  658.     if (tokpushback) {
  659.         tokpushback = 0;
  660.         return lasttoken;
  661.     }
  662.     if (needprompt) {
  663.         putprompt(ps2val());
  664.         needprompt = 0;
  665.     }
  666.     startlinno = plinno;
  667.     for (;;) {    /* until token or start of word found */
  668.         c = pgetc_macro();
  669.         if (c == ' ' || c == '\t')
  670.             continue;        /* quick check for white space first */
  671.         switch (c) {
  672.         case ' ': case '\t':
  673.             continue;
  674.         case '#':
  675.             while ((c = pgetc()) != '\n' && c != PEOF);
  676.             pungetc();
  677.             continue;
  678.         case '\\':
  679.             if (pgetc() == '\n') {
  680.                 startlinno = ++plinno;
  681.                 if (doprompt)
  682.                     putprompt(ps2val());
  683.                 continue;
  684.             }
  685.             pungetc();
  686.             goto breakloop;
  687.         case '\n':
  688.             plinno++;
  689.             needprompt = doprompt;
  690.             RETURN(TNL);
  691.         case PEOF:
  692.             RETURN(TEOF);
  693.         case '&':
  694.             if (pgetc() == '&')
  695.                 RETURN(TAND);
  696.             pungetc();
  697.             RETURN(TBACKGND);
  698.         case '|':
  699.             if (pgetc() == '|')
  700.                 RETURN(TOR);
  701.             pungetc();
  702.             RETURN(TPIPE);
  703.         case ';':
  704.             if (pgetc() == ';')
  705.                 RETURN(TENDCASE);
  706.             pungetc();
  707.             RETURN(TSEMI);
  708.         case '(':
  709.             RETURN(TLP);
  710.         case ')':
  711.             RETURN(TRP);
  712.         default:
  713.             goto breakloop;
  714.         }
  715.     }
  716. breakloop:
  717.     return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
  718. #undef RETURN
  719. }
  720.  
  721.  
  722.  
  723. /*
  724.  * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
  725.  * is not NULL, read a here document.  In the latter case, eofmark is the
  726.  * word which marks the end of the document and striptabs is true if
  727.  * leading tabs should be stripped from the document.  The argument firstc
  728.  * is the first character of the input token or document.
  729.  *
  730.  * Because C does not have internal subroutines, I have simulated them
  731.  * using goto's to implement the subroutine linkage.  The following macros
  732.  * will run code that appears at the end of readtoken1.
  733.  */
  734.  
  735. #define CHECKEND()    {goto checkend; checkend_return:;}
  736. #define PARSEREDIR()    {goto parseredir; parseredir_return:;}
  737. #define PARSESUB()    {goto parsesub; parsesub_return:;}
  738. #define PARSEBACKQOLD()    {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
  739. #define PARSEBACKQNEW()    {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
  740.  
  741. STATIC int
  742. readtoken1(firstc, syntax, eofmark, striptabs)
  743.     int firstc;
  744.     char const *syntax;
  745.     char *eofmark;
  746.     int striptabs;
  747.     {
  748.     register c = firstc;
  749.     register char *out;
  750.     int len;
  751.     char line[EOFMARKLEN + 1];
  752.     struct nodelist *bqlist;
  753.     int quotef;
  754.     int dblquote;
  755.     int varnest;
  756.     int oldstyle;
  757.  
  758.     startlinno = plinno;
  759.     dblquote = 0;
  760.     if (syntax == DQSYNTAX)
  761.         dblquote = 1;
  762.     quotef = 0;
  763.     bqlist = NULL;
  764.     varnest = 0;
  765.     STARTSTACKSTR(out);
  766.     loop: {    /* for each line, until end of word */
  767. #if ATTY
  768.         if (c == '\034' && doprompt
  769.          && attyset() && ! equal(termval(), "emacs")) {
  770.             attyline();
  771.             if (syntax == BASESYNTAX)
  772.                 return readtoken();
  773.             c = pgetc();
  774.             goto loop;
  775.         }
  776. #endif
  777.         CHECKEND();    /* set c to PEOF if at end of here document */
  778.         for (;;) {    /* until end of line or end of word */
  779.             CHECKSTRSPACE(3, out);    /* permit 3 calls to USTPUTC */
  780.             switch(syntax[c]) {
  781.             case CNL:    /* '\n' */
  782.                 if (syntax == BASESYNTAX)
  783.                     goto endword;    /* exit outer loop */
  784.                 USTPUTC(c, out);
  785.                 plinno++;
  786.                 if (doprompt) {
  787.                     putprompt(ps2val());
  788.                 }
  789.                 c = pgetc();
  790.                 goto loop;        /* continue outer loop */
  791.             case CWORD:
  792.                 USTPUTC(c, out);
  793.                 break;
  794.             case CCTL:
  795.                 if (eofmark == NULL || dblquote)
  796.                     USTPUTC(CTLESC, out);
  797.                 USTPUTC(c, out);
  798.                 break;
  799.             case CBACK:    /* backslash */
  800.                 c = pgetc();
  801.                 if (c == PEOF) {
  802.                     USTPUTC('\\', out);
  803.                     pungetc();
  804.                 } else if (c == '\n') {
  805.                     if (doprompt)
  806.                         putprompt(ps2val());
  807.                 } else {
  808.                     if (dblquote && c != '\\' && c != '`' && c != '$'
  809.                              && (c != '"' || eofmark != NULL))
  810.                         USTPUTC('\\', out);
  811.                     if (SQSYNTAX[c] == CCTL)
  812.                         USTPUTC(CTLESC, out);
  813.                     USTPUTC(c, out);
  814.                     quotef++;
  815.                 }
  816.                 break;
  817.             case CSQUOTE:
  818.                 syntax = SQSYNTAX;
  819.                 break;
  820.             case CDQUOTE:
  821.                 syntax = DQSYNTAX;
  822.                 dblquote = 1;
  823.                 break;
  824.             case CENDQUOTE:
  825.                 if (eofmark) {
  826.                     USTPUTC(c, out);
  827.                 } else {
  828.                     syntax = BASESYNTAX;
  829.                     quotef++;
  830.                     dblquote = 0;
  831.                 }
  832.                 break;
  833.             case CVAR:    /* '$' */
  834.                 PARSESUB();        /* parse substitution */
  835.                 break;
  836.             case CENDVAR:    /* '}' */
  837.                 if (varnest > 0) {
  838.                     varnest--;
  839.                     USTPUTC(CTLENDVAR, out);
  840.                 } else {
  841.                     USTPUTC(c, out);
  842.                 }
  843.                 break;
  844.             case CBQUOTE:    /* '`' */
  845.                 if (parsebackquote && syntax == BASESYNTAX) {
  846.                     if (out == stackblock())
  847.                         return lasttoken = TENDBQUOTE;
  848.                     else
  849.                         goto endword;    /* exit outer loop */
  850.                 }
  851.                 PARSEBACKQOLD();
  852.                 break;
  853.             case CEOF:
  854.                 goto endword;        /* exit outer loop */
  855.             default:
  856.                 if (varnest == 0)
  857.                     goto endword;    /* exit outer loop */
  858.                 USTPUTC(c, out);
  859.             }
  860.             c = pgetc_macro();
  861.         }
  862.     }
  863. endword:
  864.     if (syntax != BASESYNTAX && eofmark == NULL)
  865.         synerror("Unterminated quoted string");
  866.     if (varnest != 0) {
  867.         startlinno = plinno;
  868.         synerror("Missing '}'");
  869.     }
  870.     USTPUTC('\0', out);
  871.     len = out - stackblock();
  872.     out = stackblock();
  873.     if (eofmark == NULL) {
  874.         if ((c == '>' || c == '<')
  875.          && quotef == 0
  876.          && len <= 2
  877.          && (*out == '\0' || is_digit(*out))) {
  878.             PARSEREDIR();
  879.             return lasttoken = TREDIR;
  880.         } else {
  881.             pungetc();
  882.         }
  883.     }
  884.     quoteflag = quotef;
  885.     backquotelist = bqlist;
  886.     grabstackblock(len);
  887.     wordtext = out;
  888.     return lasttoken = TWORD;
  889. /* end of readtoken routine */
  890.  
  891.  
  892.  
  893. /*
  894.  * Check to see whether we are at the end of the here document.  When this
  895.  * is called, c is set to the first character of the next input line.  If
  896.  * we are at the end of the here document, this routine sets the c to PEOF.
  897.  */
  898.  
  899. checkend: {
  900.     if (eofmark) {
  901.         if (striptabs) {
  902.             while (c == '\t')
  903.                 c = pgetc();
  904.         }
  905.         if (c == *eofmark) {
  906.             if (pfgets(line, sizeof line) != NULL) {
  907.                 register char *p, *q;
  908.  
  909.                 p = line;
  910.                 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
  911.                 if (*p == '\n' && *q == '\0') {
  912.                     c = PEOF;
  913.                     plinno++;
  914.                     needprompt = doprompt;
  915.                 } else {
  916.                     ppushback(line, strlen(line));
  917.                 }
  918.             }
  919.         }
  920.     }
  921.     goto checkend_return;
  922. }
  923.  
  924.  
  925. /*
  926.  * Parse a redirection operator.  The variable "out" points to a string
  927.  * specifying the fd to be redirected.  The variable "c" contains the
  928.  * first character of the redirection operator.
  929.  */
  930.  
  931. parseredir: {
  932.     char fd = *out;
  933.     union node *np;
  934.  
  935.     np = (union node *)stalloc(sizeof (struct nfile));
  936.     if (c == '>') {
  937.         np->nfile.fd = 1;
  938.         c = pgetc();
  939.         if (c == '>')
  940.             np->type = NAPPEND;
  941.         else if (c == '&')
  942.             np->type = NTOFD;
  943.         else {
  944.             np->type = NTO;
  945.             pungetc();
  946.         }
  947.     } else {    /* c == '<' */
  948.         np->nfile.fd = 0;
  949.         c = pgetc();
  950.         if (c == '<') {
  951.             if (sizeof (struct nfile) != sizeof (struct nhere)) {
  952.                 np = (union node *)stalloc(sizeof (struct nhere));
  953.                 np->nfile.fd = 0;
  954.             }
  955.             np->type = NHERE;
  956.             heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
  957.             heredoc->here = np;
  958.             if ((c = pgetc()) == '-') {
  959.                 heredoc->striptabs = 1;
  960.             } else {
  961.                 heredoc->striptabs = 0;
  962.                 pungetc();
  963.             }
  964.         } else if (c == '&')
  965.             np->type = NFROMFD;
  966.         else {
  967.             np->type = NFROM;
  968.             pungetc();
  969.         }
  970.     }
  971.     if (fd != '\0')
  972.         np->nfile.fd = digit_val(fd);
  973.     redirnode = np;
  974.     goto parseredir_return;
  975. }
  976.  
  977.  
  978. /*
  979.  * Parse a substitution.  At this point, we have read the dollar sign
  980.  * and nothing else.
  981.  */
  982.  
  983. parsesub: {
  984.     int subtype;
  985.     int typeloc;
  986.     int flags;
  987.     char *p;
  988. #ifndef GDB_HACK
  989.     static const char types[] = "}-+?=";
  990. #endif
  991.  
  992.     c = pgetc();
  993.     if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
  994.         USTPUTC('$', out);
  995.         pungetc();
  996.     } else if (c == '(') {    /* $(command) */
  997.         PARSEBACKQNEW();
  998.     } else {
  999.         USTPUTC(CTLVAR, out);
  1000.         typeloc = out - stackblock();
  1001.         USTPUTC(VSNORMAL, out);
  1002.         subtype = VSNORMAL;
  1003.         if (c == '{') {
  1004.             c = pgetc();
  1005.             subtype = 0;
  1006.         }
  1007.         if (is_name(c)) {
  1008.             do {
  1009.                 STPUTC(c, out);
  1010.                 c = pgetc();
  1011.             } while (is_in_name(c));
  1012.         } else {
  1013.             if (! is_special(c))
  1014. badsub:                synerror("Bad substitution");
  1015.             USTPUTC(c, out);
  1016.             c = pgetc();
  1017.         }
  1018.         STPUTC('=', out);
  1019.         flags = 0;
  1020.         if (subtype == 0) {
  1021.             if (c == ':') {
  1022.                 flags = VSNUL;
  1023.                 c = pgetc();
  1024.             }
  1025.             p = strchr(types, c);
  1026.             if (p == NULL)
  1027.                 goto badsub;
  1028.             subtype = p - types + VSNORMAL;
  1029.         } else {
  1030.             pungetc();
  1031.         }
  1032.         if (dblquote)
  1033.             flags |= VSQUOTE;
  1034.         *(stackblock() + typeloc) = subtype | flags;
  1035.         if (subtype != VSNORMAL)
  1036.             varnest++;
  1037.     }
  1038.     goto parsesub_return;
  1039. }
  1040.  
  1041.  
  1042. /*
  1043.  * Called to parse command substitutions.  Newstyle is set if the command
  1044.  * is enclosed inside $(...); nlpp is a pointer to the head of the linked
  1045.  * list of commands (passed by reference), and savelen is the number of
  1046.  * characters on the top of the stack which must be preserved.
  1047.  */
  1048.  
  1049. parsebackq: {
  1050.     struct nodelist **nlpp;
  1051.     int savepbq;
  1052.     union node *n;
  1053.     char *volatile str;
  1054.     struct jmploc jmploc;
  1055.     struct jmploc *volatile savehandler;
  1056.     int savelen;
  1057.     int t;
  1058.  
  1059.     savepbq = parsebackquote;
  1060.     if (setjmp(jmploc.loc)) {
  1061.         if (str)
  1062.             ckfree(str);
  1063.         parsebackquote = 0;
  1064.         handler = savehandler;
  1065.         longjmp(handler, 1);
  1066.     }
  1067.     INTOFF;
  1068.     str = NULL;
  1069.     savelen = out - stackblock();
  1070.     if (savelen > 0) {
  1071.         str = ckmalloc(savelen);
  1072.         bcopy(stackblock(), str, savelen);
  1073.     }
  1074.     savehandler = handler;
  1075.     handler = &jmploc;
  1076.     INTON;
  1077.     nlpp = &bqlist;
  1078.     while (*nlpp)
  1079.         nlpp = &(*nlpp)->next;
  1080.     *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
  1081.     (*nlpp)->next = NULL;
  1082.     parsebackquote = oldstyle;
  1083.     n = list(0);
  1084.     t = oldstyle? TENDBQUOTE : TRP;
  1085.     if (readtoken() != t)
  1086.         synexpect(t);
  1087.     (*nlpp)->n = n;
  1088.     while (stackblocksize() <= savelen)
  1089.         growstackblock();
  1090.     STARTSTACKSTR(out);
  1091.     if (str) {
  1092.         bcopy(str, out, savelen);
  1093.         STADJUST(savelen, out);
  1094.         INTOFF;
  1095.         ckfree(str);
  1096.         str = NULL;
  1097.         INTON;
  1098.     }
  1099.     parsebackquote = savepbq;
  1100.     handler = savehandler;
  1101.     USTPUTC(CTLBACKQ + dblquote, out);
  1102.     if (oldstyle)
  1103.         goto parsebackq_oldreturn;
  1104.     else
  1105.         goto parsebackq_newreturn;
  1106. }
  1107.  
  1108. } /* end of readtoken */
  1109.  
  1110.  
  1111.  
  1112. #ifdef mkinit
  1113. RESET {
  1114.     tokpushback = 0;
  1115. }
  1116. #endif
  1117.  
  1118.  
  1119. #if ATTY
  1120. /*
  1121.  * Called to process a command generated by atty.  We execute the line,
  1122.  * and catch any errors that occur so they don't propagate outside of
  1123.  * this routine.
  1124.  */
  1125.  
  1126. STATIC void
  1127. attyline() {
  1128.     char line[256];
  1129.     struct stackmark smark;
  1130.     struct jmploc jmploc;
  1131.     struct jmploc *volatile savehandler;
  1132.  
  1133.     if (pfgets(line, sizeof line) == NULL)
  1134.         return;                /* "can't happen" */
  1135.     if (setjmp(jmploc.loc)) {
  1136.         if (exception == EXERROR)
  1137.             out2str("\033]D\n");
  1138.         handler = savehandler;
  1139.         longjmp(handler, 1);
  1140.     }
  1141.     savehandler = handler;
  1142.     handler = &jmploc;
  1143.     setstackmark(&smark);
  1144.     evalstring(line);
  1145.     popstackmark(&smark);
  1146.     handler = savehandler;
  1147.     doprompt = 1;
  1148. }
  1149.  
  1150.  
  1151. /*
  1152.  * Output a prompt for atty.  We output the prompt as part of the
  1153.  * appropriate escape sequence.  
  1154.  */
  1155.  
  1156. STATIC void
  1157. putprompt(s)
  1158.     char *s;
  1159.     {
  1160.     register char *p;
  1161.  
  1162.     if (attyset() && ! equal(termval(), "emacs")) {
  1163.         if (strchr(s, '\7'))
  1164.             out2c('\7');
  1165.         out2str("\033]P1;");
  1166.         for (p = s ; *p ; p++) {
  1167.             if ((unsigned)(*p - ' ') <= '~' - ' ')
  1168.                 out2c(*p);
  1169.         }
  1170.         out2c('\n');
  1171.     } else {
  1172.         out2str(s);
  1173.     }
  1174. }
  1175. #endif
  1176.  
  1177.  
  1178.  
  1179. /*
  1180.  * Returns true if the text contains nothing to expand (no dollar signs
  1181.  * or backquotes).
  1182.  */
  1183.  
  1184. STATIC int
  1185. noexpand(text)
  1186.     char *text;
  1187.     {
  1188.     register char *p;
  1189.     register char c;
  1190.  
  1191.     p = text;
  1192.     while ((c = *p++) != '\0') {
  1193.         if (c == CTLESC)
  1194.             p++;
  1195.         else if (BASESYNTAX[c] == CCTL)
  1196.             return 0;
  1197.     }
  1198.     return 1;
  1199. }
  1200.  
  1201.  
  1202. /*
  1203.  * Return true if the argument is a legal variable name (a letter or
  1204.  * underscore followed by zero or more letters, underscores, and digits).
  1205.  */
  1206.  
  1207. int
  1208. goodname(name)
  1209.     char *name;
  1210.     {
  1211.     register char *p;
  1212.  
  1213.     p = name;
  1214.     if (! is_name(*p))
  1215.         return 0;
  1216.     while (*++p) {
  1217.         if (! is_in_name(*p))
  1218.             return 0;
  1219.     }
  1220.     return 1;
  1221. }
  1222.  
  1223.  
  1224. /*
  1225.  * Called when an unexpected token is read during the parse.  The argument
  1226.  * is the token that is expected, or -1 if more than one type of token can
  1227.  * occur at this point.
  1228.  */
  1229.  
  1230. STATIC void
  1231. synexpect(token) {
  1232.     char msg[64];
  1233.  
  1234.     if (token >= 0) {
  1235.         fmtstr(msg, 64, "%s unexpected (expecting %s)",
  1236.             tokname[lasttoken], tokname[token]);
  1237.     } else {
  1238.         fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
  1239.     }
  1240.     synerror(msg);
  1241. }
  1242.  
  1243.  
  1244. STATIC void
  1245. synerror(msg)
  1246.     char *msg;
  1247.     {
  1248.     if (commandname)
  1249.         outfmt(&errout, "%s: %d: ", commandname, startlinno);
  1250.     outfmt(&errout, "Syntax error: %s\n", msg);
  1251.     error((char *)NULL);
  1252. }
  1253.